import os
from ctypes import *

cwd = os.getcwd() + "/PushkinDll.dll"
lib = CDLL(cwd)


# 定义缠论配置选项结构体
class ChanConfig(Structure):
    _fields_ = [
        ('ChanKind', c_int),            # 缠论类型。0 - 线中枢、1 - 笔中枢 - 1、2 - 笔中枢维持 - 2、3 - 笔中枢 - 3、4 - 笔中枢维持 - 4
        ('TypeBaoHan', c_int),          # 包含关系的处理方法。1 - 经典理论法；2 - 逐K线分析法
        ('TypeBi1', c_int),             # 笔划分：划分前处理包含关系。1 - 处理；0 - 不处理
        ('TypeBi2', c_int),             # 笔划分：进行笔的修正。1 - 修正；0 - 不修正
        ('TypeBi3', c_int),             # 笔划分：允许次高(低)点成笔。1 - 允许；0 - 不允许
        ('TypeBi4', c_int),             # 笔划分：允许缺口成笔。1 - 允许；0 - 不允许
        ('TypeBi5', c_int),             # 笔划分：允许顶底K线重叠。1 - 允许；0 - 不允许
        ('TypeQuekou', c_int),          # 允许带缺口的笔升为线段。1 - 允许；0 - 不允许
        ('TypeBiaoZhunHua', c_int),     # 对结果进行标准化处理。1 - 处理；0 - 不处理
        ('TypeZhongBreak', c_int)       # 中枢划分方法。1 - 限制延伸，9段中枢结束；2 - 不限制延伸，中枢可超过9段
        ]


# 定义缠论笔节点结构体
class ChanPotBi(Structure):
    _fields_ = [
        ('fenxing', c_byte),    # 分型标记。1：顶；-1：底；0：非顶底（非顶底时其它域没有意义）

        ('zhshg', c_float),     # 中枢高 / 低
        ('zhshd', c_float),
        ('zhshgg', c_float),    # 中枢最高 / 最低
        ('zhshdd', c_float),
        ('zhsh', c_byte),       # 中枢首尾标记。中枢开始最低位为1；中枢结束次低位为1

        ('beichi', c_byte)      # 笔背驰标记。取值范围：-9~9。取值含义见《普吸金缠论大全》
        ]


# 定义缠论线段节点结构体
class ChanPotXian(Structure):
    _fields_ = [
        ('fenxing', c_byte),    # 分型标记。1：顶；-1：底；0：非顶底（非顶底时其它域没有意义）
        ('xian', c_byte),       # 线段标记。取值与fenxing相同

        ('zhshg', c_float),     # 中枢高 / 低
        ('zhshd', c_float),
        ('zhshgg', c_float),    # 中枢最高 / 最低
        ('zhshdd', c_float),
        ('zhsh', c_byte),       # 中枢首尾标记。中枢开始最低位为1；中枢结束次低位为1

        ('xianmm1', c_byte),    # 买卖点标记：一类。-1是买点，1是卖点，0不是买卖点
        ('xianmm2', c_byte),    # 买卖点标记：二三
        ('xianmm3', c_byte),    # 买卖点标记：三类
        ('xianmp1', c_byte),    # 买卖点标记：盘一
        ('xianmp2', c_byte),    # 买卖点标记：盘二
        ('xianml2', c_byte),    # 买卖点标记：类二
        ('xianpu2', c_byte),    # 买卖点标记：普二
        ('beichi', c_byte)      # 线段背驰标记。取值范围：-9~9。取值含义见《普吸金缠论大全》
        ]
# 缠论买卖点在线段节点上标注。-1表示买点，1表示卖点，0表示无买卖点。
# 背驰是一个数值，0表示无背驰，<0表示底背驰，>0表示顶背驰，绝对值越大，表示背驰越明显。


# 定义缠论分析结果结构体
# 返回结果主要是对当下买卖点的描述
class ChanResult(Structure):
    _fields_ = [
        # 经典缠论买卖点在线段节点中标记，以下两域是对当下经典缠论买卖点的描述
        ('m_Signal0', c_int),  # 缠论买卖点信号：0-无买/卖点；1-买卖点结构形成；2-分型；3-分型之后；4、5-分型停顿；6-停顿之后
        ('m_Mark0', c_float),  # 缠论买点得分。买点为负，卖点为正，绝对值越大越好

        ('m_MarkDay', c_float),  # 当前的日线K线得分

        # 以下是对三类扩展买点的描述
        ('xiankz1', c_int),     # 扩展买点标记（只有买点，没有卖点）。-1：买点；0：无买点
        ('xiankz2', c_int),
        ('xiankz3', c_int),
        ('m_Signal1', c_int),  # 扩展买点1信号
        ('m_Signal2', c_int),  # 扩展买点2信号
        ('m_Signal3', c_int),  # 扩展买点3信号
        ('m_Mark1', c_float),  # 扩展买点1得分。买点值为负，绝对值越大越好
        ('m_Mark2', c_float),  # 扩展买点2得分
        ('m_Mark3', c_float),  # 扩展买点3得分
    ]
# 扩展买点为定制买点，不同买点的信号值定义各不相同。总得来说，越发展，值越大。


class BarManager:
    """K线管理器"""

    def __init__(self):
        """初始化"""
        self.num_max = 2000    # 允许的数据量
        self.inOpen = (c_float * self.num_max)()
        self.inHigh = (c_float * self.num_max)()
        self.inLow = (c_float * self.num_max)()
        self.inClose = (c_float * self.num_max)()
        self.inVolume = (c_float * self.num_max)()
        self.outBi = (ChanPotBi * self.num_max)()
        self.outXian = (ChanPotXian * self.num_max)()
        self.nums = 0    # 当前数据量
        self.outResult = ChanResult()
        self.errNo = c_int(0)  # 不能省略

    def update_bar(self, bar) -> None:
        """
        接受一个K线数据
        """
        # 取得日期时间
        # dt = bar.bob

        if self.nums < (self.num_max - 1):
            self.inOpen[self.nums] = bar.open
            self.inHigh[self.nums] = bar.high
            self.inLow[self.nums] = bar.low
            self.inClose[self.nums] = bar.close
            self.inVolume[self.nums] = bar.volume
            self.nums = self.nums + 1
        else:
            for i in range(self.num_max - 1):
                self.inOpen[i] = self.inOpen[i + 1]
                self.inHigh[i] = self.inHigh[i+ 1]
                self.inLow[i] = self.inLow[i+ 1]
                self.inClose[i] = self.inClose[i+ 1]
                self.inVolume[i] = self.inVolume[i+ 1]

            self.inOpen[self.nums] = bar.open
            self.inHigh[self.nums] = bar.high
            self.inLow[self.nums] = bar.low
            self.inClose[self.nums] = bar.close
            self.inVolume[self.nums] = bar.volume

        # 重新计算缠论分析结果
        self.recalc_chan()

    def recalc_chan(self):
        """
        重新计算缠论分析结果
        """
        # 配置普吸金缠论选项
        # self.chan_config()
        # 普吸金缠论有5种分析方法，本示例没有进行配置，使用默认的方法0。
        # 使用其它方法，或多种方法组合效果更好。
        # 使用其它方法需要授权，相关内容可参考《普吸金缠论大全》

        lib.pushkin_chan.argtypes = [c_int, POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float),
                                     POINTER(c_float), c_void_p, c_void_p, POINTER(ChanResult), POINTER(c_int)]
        lib.pushkin_chan(self.nums, self.inOpen, self.inHigh, self.inLow, self.inClose,
                         self.inVolume, self.outBi, self.outXian, self.outResult, self.errNo)
        
    def chan_config(self):
        cc = ChanConfig()
        cc.ChanKind = 1
        cc.TypeBaoHan = 2
        cc.TypeBi1 = 1
        cc.TypeBi2 = 1
        cc.TypeBi3 = 1
        cc.TypeBi4 = 1
        cc.TypeBi5 = 1
        cc.TypeQuekou = 1
        cc.TypeBiaoZhunHua = 0
        cc.TypeZhongBreak = 2
        # 配置成功后，返回所设置的缠论类型
        chan_kind = lib.set_config(cc)

    def is_buy_point(self):
        """返回开仓信息"""
        ret = 0
        cancel_price = 0.0

        # 向前找最后一个线段结束
        for i in range(self.nums - 1, 0, -1):
            # 找到了线段结束
            if self.outXian[i].fenxing != 0:
                # 如果是分型停顿
                if self.outResult.m_Signal0 == 4:
                    # 只在趋一、盘一和类二时开仓
                    ret = self.outXian[i].xianmm1 + self.outXian[i].xianmp1 + self.outXian[i].xianml2
                    # 以线段的极值作为止损价
                    if ret < 0:     # 买点
                        cancel_price = self.inLow[i]
                    elif ret > 0:   # 卖点
                        cancel_price = self.inHigh[i]
                break

        # 返回开仓信息和止损价
        return ret, cancel_price

    def is_sell_point(self):
        """返回平仓信息"""
        ret = 0
        # 向前找最后一个线段结束
        for i in range(self.nums - 1, 0, -1):
            # 找到了线段结束
            if self.outXian[i].fenxing != 0:
                # 如果已经出了分型
                if self.outResult.m_Signal0 >= 1:
                    # 任何买卖点都平仓
                    ret = self.outXian[i].xianmm1 + self.outXian[i].xianmm2 + self.outXian[i].xianmm3 \
                        + self.outXian[i].xianmp1 + self.outXian[i].xianmp2 \
                        + self.outXian[i].xianpu2 + self.outXian[i].beichi
                break
        
        # 返回平仓信息
        return ret
